package com.navinfo.data.journal;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.*;

/**
 * 车辆趟程发现，及其平均运程计算
 * V4：
 * 1.利用vertex的时间点进行趟程切分，不采用dfs算法
 * 2.进行趟程节点循环相同趟程的key合并
 * 3.使用瓦片/城市ID来jvs节点表
 */
public class VehicleJournalV4 {
    private static final Logger LOGGER = LoggerFactory.getLogger(VehicleJournalV4.class);

    /**
     * 运距(米)
     */
    private static int LOAD_DISTANCE = 5*1000;
    /**
     * 瓦片级别
     */
    private static int ZOOM = 12;
    /**
     * JournalVertex集合
     */
    private static List<JournalVertex> jvs = new ArrayList<JournalVertex>();
    /**
     * JournalEdge集合
     */
    private static Map<String,JournalEdge> jes = new HashMap<String,JournalEdge>();
    /**
     * 瓦片城市对应集合
     */
    private static Map<Long, Integer> tileLcMapping = new HashMap<Long, Integer>();

    /**
     * 根据经纬度获取瓦片ID
     *
     * @param lat           经度
     * @param lon           纬度
     * @return 瓦片ID
     */
    public static long getLc(int lat, int lon) {
        Long tileIdMin = VehiclePassTimesQuadtree.getTileNumber(lat, lon, ZOOM);

        return tileIdMin;
    }

    /**
     * add or find SimilarVertex
     *
     * @param lat2 经度
     * @param lng2 维度
     * @param time 时间点
     * @param flag 0开始时间点，1结束时间点
     * @return list下标
     */
    private static long afSimilarVertex(double lat2, double lng2, int time,int flag) {
        int result = -1;
        long id1 = getLc((int)(lat2*1000000), (int)(lng2*1000000));
        for(int i = 0 ; i < jvs.size() ; i++) {
            JournalVertex jv = jvs.get(i);
            long id2 = jv.getTileNumber();
            if (id1==id2) {
                jv.getTimelist().add(time+":"+flag);
                jvs.set(i,jv);
                result = i;
                break;
            }
        }
        if (result==-1) {
            JournalVertex jv =new JournalVertex(id1);
            jv.getTimelist().add(time+":"+flag);
            jv.setLat(lat2);
            jv.setLng(lng2);
            jvs.add(jv);
            result = jvs.size()-1;
        }

        return id1;
    }

    /**
     * add or update JournalEdge
     */
    private static void afJournalEdge(long src, long dst, int totalDistance, int routeStartTime, int routeEndTime,int vehicleEstimatedLoad) {
        String mapkey = src + "-" + dst;
        if (jes.containsKey(mapkey)) {
            JournalEdge je = jes.get(mapkey);
            je.setTimes(je.getTimes() + 1);
            je.setTotalDistance(je.getTotalDistance() + totalDistance);
            je.setTotalVehicleEstimatedLoad(je.getTotalVehicleEstimatedLoad()+vehicleEstimatedLoad);
            je.setTotalDuration(je.getTotalDuration() + (routeEndTime - routeStartTime));
            jes.put(mapkey, je);
        } else {
            JournalEdge je = new JournalEdge(src, dst, totalDistance, 1, routeEndTime - routeStartTime,vehicleEstimatedLoad);
            jes.put(mapkey, je);
        }
    }

    /**
     * 使用 Map按key进行排序
     * @param map
     * @return
     */
    public static Map<String, JournalDescV3> sortMapByKey(Map<String, JournalDescV3> map) {
        if (map == null || map.isEmpty()) {
            return null;
        }

        Map<String, JournalDescV3> sortMap = new TreeMap<String, JournalDescV3>(
                new MapKeyComparator());

        sortMap.putAll(map);

        return sortMap;
    }

    /**
     * 判断是否存在已有的mapkey，如果存在返回map中的key，否则返回null
     * @param journalDescV3Map
     * @param points
     * @return mapkey
     */
    private static String getSameKeyFromMap(Map<String,JournalDescV3> journalDescV3Map, String points) {
        LOGGER.debug("---------------s------------------------");
        LOGGER.debug(points);
        LOGGER.debug("---------------m------------------------");
        for (Map.Entry<String, JournalDescV3> entry : journalDescV3Map.entrySet()) {
            LOGGER.debug("Key = " + entry.getKey() + ", Value = " + entry.getValue().toString());
        }
        LOGGER.debug("---------------e------------------------");
        String result = null;
        String[] pointArray = points.split("->");
        String [] newPointArray;
        newPointArray = Arrays.copyOfRange(pointArray, 0, pointArray.length-1);
        LOGGER.debug("left:"+Arrays.toString(newPointArray));

        OUT:
        for (Map.Entry<String, JournalDescV3> entry : journalDescV3Map.entrySet()) {
//            LOGGER.info("Key = " + entry.getKey() + ", Value = " + entry.getValue().toString());
            String [] data = entry.getKey().split("->");
            if (pointArray.length!=data.length) {
                continue;
            }
            String[] newData;
            newData = Arrays.copyOfRange(data, 0, data.length-1);
            List list = new ArrayList(Arrays.asList(newData));
            for(int i = 0 ; i < list.size() ; i++) {
                for(int j = 0 ; j < i ; j++) {
                    String tmp = (String)list.get(0);
                    list.remove(0);
                    list.add(tmp);
                }
                Object[] c = list.toArray();
                LOGGER.debug("right:"+Arrays.toString(c));
                if (Arrays.equals(newPointArray, c)) {
                    result = entry.getKey();
                    break OUT;
                }
            }
        }
        LOGGER.debug("result:"+result);
        return result;
    }

    /**
     * 将趟程（各类节点顺序组合）放入journalDescV3Map
     * @param journalDescV3Map
     * @param points
     * @param totalDistance
     * @param totalDuration
     */
    private static void handleJournalDescV3(Map<String,JournalDescV3> journalDescV3Map, String points, int totalDistance, int totalDuration) {
        LOGGER.debug("totalDuration:"+totalDuration);
        String result = getSameKeyFromMap(journalDescV3Map,points);
        if (result !=null) {
            JournalDescV3 jdv3 = journalDescV3Map.get(result);
            jdv3.setTcTimes(jdv3.getTcTimes()+1);
            jdv3.setTotalDistance(jdv3.getTotalDistance()+totalDistance);
            jdv3.setTotalDuration(jdv3.getTotalDuration()+totalDuration);
            journalDescV3Map.put(result,jdv3);
        } else {
            JournalDescV3 jdv3 = new JournalDescV3(points,totalDuration,totalDistance,1);
            journalDescV3Map.put(points,jdv3);
        }
    }


    public static void main(String[] args) {
        Reader in = null;
        Iterable<CSVRecord> records = null;
        /*//read lc map data start------------------------
        try {
            in = new FileReader("E:\\qingqidata\\navinfo_tile_lc_mapping\\data.csv");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            LOGGER.error("FileNotFoundException");
        }
        try {
            records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in);
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error("IOException");
        }

        for (CSVRecord record : records) {
            tileLcMapping.put(Long.parseLong(record.get("lc_id")),Integer.parseInt(record.get("city_id")));
        }
        LOGGER.info("tileLcMapping.size:"+tileLcMapping.size());

        //read lc map data end------------------------*/
        //read trip data start------------------------
        try {
            //百事车辆
            in = new FileReader("E:\\qingqidata\\10019411330\\10019411330驾驶行为分析(2018-11-07-00-00-00至2018-11-12-00-00-00).csv");
            //节油大赛车辆
//            in = new FileReader("E:\\qingqidata\\10021042261驾驶行为分析(2018-09-27-00-00-00至2018-09-29-00-00-00).csv");
            //随便找的一台车
//            in = new FileReader("E:\\qingqidata\\10020435809驾驶行为分析(2018-12-10-00-00-00至2018-12-12-00-00-00).csv");
            //随便找的一台of37车
//            in = new FileReader("E:\\qingqidata\\10019410082驾驶行为分析(2018-12-10-00-00-00至2018-12-12-00-00-00).csv");
            //随便找的一台of37车（14天发现闭环）
//            in = new FileReader("E:\\qingqidata\\14705564807\\14705564807驾驶行为分析(2018-12-04-00-00-00至2018-12-18-00-00-00).csv");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            LOGGER.error("FileNotFoundException");
        }

        try {
            records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in);
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error("IOException");
        }

        //行程records需要按照行程时间排序！！！！！！！！！！！！！！！！！！！
        List<TripRecord> tripRecordList = new ArrayList<TripRecord>();
        for (CSVRecord record : records) {
            double routeStartLatitude =  Double.parseDouble(record.get("routeStartLatitude"))/1000000;
            double routeStartLongitude =  Double.parseDouble(record.get("routeStartLongitude"))/1000000;
            double routeEndLatitude =  Double.parseDouble(record.get("routeEndLatitude"))/1000000;
            double routeEndLongitude =  Double.parseDouble(record.get("routeEndLongitude"))/1000000;
            int routeIntegralMileage =  Integer.parseInt(record.get("routeIntegralMileage"));
            int routeStartTime =  Integer.parseInt(record.get("routeStartTime"));
            int routeEndTime =  Integer.parseInt(record.get("routeEndTime"));
            int vehicleEstimatedLoad = Integer.parseInt(record.get("vehicleEstimatedLoad"));

            long src = afSimilarVertex(routeStartLatitude,routeStartLongitude,routeStartTime,0);
            long dst = afSimilarVertex(routeEndLatitude,routeEndLongitude,routeEndTime,1);
            afJournalEdge(src, dst, routeIntegralMileage, routeStartTime, routeEndTime,vehicleEstimatedLoad);

            tripRecordList.add(new TripRecord(src,dst,routeIntegralMileage,routeStartTime,routeEndTime));
        }
        //read trip data end------------------------

        LOGGER.info("-----------打印所有行程记录--------------");
        for(int j = 0 ; j < tripRecordList.size() ; j++) {
            LOGGER.info(tripRecordList.get(j).toString());
        }

        LOGGER.info("-----------打印所有趟程停靠点--------------");
        Map<String,JournalDescV3> journalDescV3Map = new HashMap<String,JournalDescV3>();
        int num = 0;
        long lastNum = -1;//上一点标号
        for(JournalVertex jv : jvs) {
            LOGGER.info("Number:"+num+", "+jv.toString());
            long currentNode = jv.getTileNumber();

            List<String> timelist = jv.getTimelist();
            int totalDuration = 0;
            int totalDistance = 0;
//            int tcTimes = 0;//趟程次数
            int durationFlag = 0;//趟程标志
            int computeFlag = 0;//计算标志
            int durationStartTime = 0;
            int durationEndTime = 0;
            StringBuffer points = new StringBuffer();
            for(int i = 0 ; i < timelist.size() ; i++) {
                if (durationFlag==0) {//尚无趟程
                    if (Integer.parseInt(timelist.get(i).split(":")[1])==0) {//判断是起始时间点
                        durationStartTime = Integer.parseInt(timelist.get(i).split(":")[0]);
                        durationFlag =1;
                    }
                }
                if (durationFlag==1) {//存在趟程
                    if (Integer.parseInt(timelist.get(i).split(":")[1])==1) {//判断是结束时间点
                        durationEndTime = Integer.parseInt(timelist.get(i).split(":")[0]);
                        durationFlag=2;
//                        tcTimes++;//趟程次数
                    }
                }

                if (durationFlag==2) {
                    //存在趟程可以开始计算运程和时长
                    LOGGER.debug("durationStartTime:"+durationStartTime+"durationEndTime:"+durationEndTime);
                    totalDuration = durationEndTime-durationStartTime;
                    for(int j = 0 ; j < tripRecordList.size() ; j++) {
                        TripRecord tr = tripRecordList.get(j);

                        if ((currentNode == tr.getSrc())&&(durationStartTime==tr.getRouteStartTime())) {
                            computeFlag = 1;
                        }

                        if (computeFlag==1) {
                            if (lastNum==-1) {
                                points.append(tr.getSrc()).append("->");
                                lastNum = tr.getSrc();
                            } else {
                                if (lastNum!=tr.getSrc()) {
                                    points.append(tr.getSrc()).append("->");
                                    lastNum = tr.getSrc();
                                }
                            }



                            totalDistance = totalDistance+tr.getRouteIntegralMileage();

                            if (currentNode == tr.getDst()) {
                                if (durationEndTime==tr.getRouteEndTime()) {
                                    points.append(tr.getDst());
                                    handleJournalDescV3(journalDescV3Map,points.toString(),totalDistance,totalDuration);
                                    computeFlag = 0;
                                    durationFlag = 0;
                                    totalDuration = 0;
                                    totalDistance = 0;
                                    lastNum = -1;
                                    points = new StringBuffer();
                                    break;//跳出内循环
                                } else {
                                    //应该不存在这样情况
                                }
                            } else {
                                if (lastNum!=tr.getDst()) {
                                    points.append(tr.getDst()).append("->");
                                    lastNum = tr.getDst();
                                }
                            }
                        }

                    }
                }
            }
            num++;
        }

        LOGGER.info("-----------打印所有趟程停靠点间行程--------------");
        for (Map.Entry<String,JournalEdge> entry : jes.entrySet()) {
            LOGGER.info("Key = " + entry.getKey() + ", Value = " + entry.getValue().toString());
        }

        LOGGER.info("-----------打印所有趟程描述--------------");
        //Map<String,JournalDescV3> journalDescV3Map = new HashMap<String,JournalDescV3>();
        Map<String, JournalDescV3> journalDescV3MapSorted = sortMapByKey(journalDescV3Map);    //按Key进行排序
        for (Map.Entry<String, JournalDescV3> entry : journalDescV3MapSorted.entrySet()) {
//            LOGGER.info("Key = " + entry.getKey() + ", Value = " + entry.getValue().toString());
            LOGGER.info(entry.getValue().toString());
        }

        LOGGER.info("-----------输出该车辆的总体平均运程和时长--------------");
        int allTotalDistance =0;
        int allTotalDuration =0;
        for (Map.Entry<String, JournalDescV3> entry : journalDescV3MapSorted.entrySet()) {
            JournalDescV3 jdv3 = entry.getValue();
            allTotalDistance = allTotalDistance + jdv3.getTotalDistance();
            allTotalDuration = allTotalDuration + jdv3.getTotalDuration();
        }

        double avgJournalDistance = 0;
        double avgJournalDuration = 0;
        for (Map.Entry<String, JournalDescV3> entry : journalDescV3MapSorted.entrySet()) {
            JournalDescV3 jdv3 = entry.getValue();
            avgJournalDistance = avgJournalDistance + ((double) jdv3.getTotalDistance()/jdv3.getTcTimes())*( (double) jdv3.getTotalDistance()/allTotalDistance);
        }
        LOGGER.info("avgJournalDistance="+(int)avgJournalDistance);

        for (Map.Entry<String, JournalDescV3> entry : journalDescV3MapSorted.entrySet()) {
            JournalDescV3 jdv3 = entry.getValue();
            avgJournalDuration = avgJournalDuration + ((double) jdv3.getTotalDuration()/jdv3.getTcTimes())*( (double) jdv3.getTotalDuration()/allTotalDuration);
        }
        LOGGER.info("avgJournalDuration="+(int)avgJournalDuration);
    }
}
